﻿using Ln.Fram.Core.Extensions;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Volo.Abp.AspNetCore.Mvc;

namespace Ln.Fram.AspNetCore.UnifyResult.Fiters;

/// <summary>
/// 友好异常拦截器
/// </summary>
public sealed class FriendlyExceptionFilter : IAsyncExceptionFilter
{
	/// <summary>
	/// 异常拦截
	/// </summary>
	/// <param name="context"></param>
	/// <returns></returns>
	public async Task OnExceptionAsync(ExceptionContext context)
	{

		// 排除 WebSocket 请求处理
		if (context.HttpContext.IsWebSocketRequest()) return;

		// 如果异常在其他地方被标记了处理，那么这里不再处理
		if (context.ExceptionHandled) return;

		// 解析异常信息
		var exceptionMetadata = GetExceptionMetadata(context);

		IUnifyResultProvider unifyResult = context.GetRequiredService<IUnifyResultProvider>();
		// 执行规范化异常处理

		context.Result = unifyResult.OnException(context, exceptionMetadata);

		// 创建日志记录器

		var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<FriendlyExceptionFilter>>();

		// 记录拦截日常
		logger.LogError(context.Exception, context.Exception.Message);
	}

	/// <summary>
	/// 获取异常元数据

	/// </summary>
	/// <param name="context"></param>
	/// <returns></returns>
	public static ExceptionMetadata GetExceptionMetadata(ActionContext context)
	{
		object errorCode = default;
		object originErrorCode = default;
		object errors = default;
		object data = default;
		var statusCode = StatusCodes.Status500InternalServerError;
		var isValidationException = false; // 判断是否是验证异常

		var isFriendlyException = false;

		// 判断是否是 ExceptionContext 或者 ActionExecutedContext
		var exception = context is ExceptionContext exContext
				? exContext.Exception
				: (
						context is ActionExecutedContext edContext
								? edContext.Exception
								: default
				);

		// 判断是否是友好异常

		if (exception is UserFriendlyException friendlyException)
		{
			int statusCode2 = 500;
			int.TryParse(friendlyException.Code, out statusCode2);
			isFriendlyException = true;
			errorCode = friendlyException.Code;
			originErrorCode = friendlyException.Code;
			statusCode = statusCode2 == 0 ? 403 : statusCode2;
			isValidationException = false;
			errors = friendlyException.Message;
			data = friendlyException.Data;
		}

		return new ExceptionMetadata
		{
			StatusCode = statusCode,
			ErrorCode = errorCode,
			OriginErrorCode = originErrorCode,
			Errors = errors,
			Data = data
		};
	}
}